/**
 * 全站 http 配置
 *
 * axios 参数说明
 * - isSerialize 是否开启 form 表单提交
 * - isToken 是否需要 token
 */
import axios from 'axios'
import store from '@/store/';
import router from '@/router/'
import {ElMessage} from 'element-plus'
import {getToken, setToken} from '@/utils/auth'
import {getStore} from '@/utils/store'
import {signUtil} from '@/utils/sign-util'
import website from '@/config/website';
import {basePath, proxyPath} from "@/config/env";
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'

/**
 * 默认请求超时时间
 */
axios.defaults.timeout = 10000 * 5;

/**
 * 指定状态码范围触发 then, 其他触发 catch
 */
axios.defaults.validateStatus = function (status) {
    return status >= 200 && status <= 500;
};

/**
 * 跨域请求,允许保存 cookie
 */
axios.defaults.withCredentials = true;

/**
 * 页面加载进度条 NProgress Configuration
 */
NProgress.configure({showSpinner: false});


/**
 * 用于控制错误消息是否已经显示的标志, 和显示的时间间隔（单位：毫秒）
 * - 当前场景: 没有登录/登录过期 时突然调用多个接口,一次性弹出多个错误信息问题（只谈一次）
 */
let errorMessageDisplayed = false;
const displayTimeout = 3000;

/**
 * 不展示错误消息的接口地址集
 */
let noneMessageUrls = [proxyPath + "/api/admin/sys/login/isLoginEnvironment"]

/**
 * HTTP request 拦截
 * - 1、添加 用户认证信息
 * - 2、添加 语言
 * - 3、对请求参数进行加签,防止接口被抓包篡改 (后端启用加签后,接口没有加签将无法成功调用后端接口)
 * - 4、租户 如果需要 (默认携带,需要直接向 store 设置 tenantName 的值)
 */
axios.interceptors.request.use(config => {
    NProgress.start();
    config.headers["Tenant-Name"] = getStore({name: "tenantName"})
    config.headers[website.Authorization] = getToken();
    config.headers["Accept-Language"] = "zh";
    let timestamp = new Date().getTime();
    let sign = signUtil(config.url, config.params, config.data, timestamp)
    config.headers['timestamp'] = `${timestamp}`;
    config.headers['sign'] = `${sign}`;
    return config;
}, error => {
    return Promise.reject(error)
});


/**
 * HTTP response 拦截
 * 1、http 状态码检查 respStatusHandler
 * 2、文件类型的响应处理 respBlobHandler
 * 3、响应成功处理 respSuccessHandler
 * 4、响应失败处理 respErrorHandler
 */
axios.interceptors.response.use(res => {
    NProgress.done();
    respStatusHandler(res);
    if ((res.data != null && res.data instanceof Blob)) {
        return respBlobHandler(res);
    } else if (res.data != null && res.data.code === 200) {
        res = respSuccessHandler(res)
    } else {
        respErrorHandler(res);
    }
    return res;
}, error => {
    NProgress.done();
    return Promise.reject(new Error(error));
});

/**
 * 响应文件处理
 */
function respBlobHandler(res) {
    const reader = new FileReader();
    reader.readAsText(res.data, 'utf-8');
    reader.onload = function () {
        try {
            // 响应的 json 表示服务器出现错误返回的错误信息
            const resJson = JSON.parse(reader.result);
            ElMessage({message: resJson.msg, type: 'error'});
            return Promise.reject(new Error(resJson.msg))
        } catch (e) {
            // 是正常的文件
        }
    }
    return res;
}

/**
 * 响应成功处理
 */
function respSuccessHandler(res) {
    if (res.config != null && res.config.method !== "get" && res.config.method !== "GET" && !noneMessageUrls.includes(res.config.url)) {
        ElMessage({message: res.data.msg, type: 'success'})
    }
    // 自动刷新 token 来进行续期
    if (res.headers[website.Authorization] !== undefined && res.headers[website.Authorization] !== null) {
        let newToken = res.headers[website.Authorization];
        setToken(newToken);
    }
    return res;
}

/**
 * 响应失败处理
 * - 10000 表示没有登录过期, 10001 表示没有登录 跳登录页
 * - 控制 登录过期时的多次触发接口导致的多次弹出错误信息处理为只在N秒内弹一次
 */
function respErrorHandler(res) {
    if (res.data.code === 10000 || res.data.code === 10001) {
        if (!errorMessageDisplayed) {
            ElMessage({message: res.data.msg, type: 'error'});
            errorMessageDisplayed = true;
            setTimeout(() => {
                errorMessageDisplayed = false;
            }, displayTimeout);
        }
        store.dispatch("LogOut").then(() => {
            router.push({path: "/login"});
        });
        return Promise.reject(new Error(res.data.msg))
    }
    // 非10000 直接展示后台给的错误提示信息
    ElMessage({message: res.data.msg, type: 'error'});
    return Promise.reject(new Error(res.data.msg))
}


/**
 * 响应状态处理器
 */
function respStatusHandler(res) {
    const status = Number(res.status) || 200;
    // 如果是401则跳转到登录页面
    if (status === 401) store.dispatch('FedLogOut').then(() => router.push({path: '/login'}));
    // 如果http状态码请求为非 200 状态码默认统一处理
    if (status !== 200) {
        const message = '后台服务走丢了,请稍后重试...';
        ElMessage({message: message, type: 'error'});
        return Promise.reject(new Error(message))
    }
}

export default axios;